package com.nancal.activiti.service.impl;

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.pagehelper.util.StringUtil;
import com.nancal.activiti.diagram.CustomProcessDiagramGeneratorImpl;
import com.nancal.activiti.entity.pojo.ModelEntityImpl;
import com.nancal.activiti.service.ProcessDesignService;
import com.nancal.activiti.service.process.ProcessRepositoryService;
import com.nancal.activiti.utils.CloseableUtils;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.editor.language.json.converter.util.CollectionUtils;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.task.Task;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.List;

@Service
public class ProcessDesignServiceImpl implements ProcessDesignService {

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private TaskService taskService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private ProcessRepositoryService processRepositoryService;
    @Autowired
    private CustomProcessDiagramGeneratorImpl processDiagramGenerator;


    /**
     * 有自定义属性的创建模型
     *
     * @param key
     * @param name
     * @param module 模块（自定义属性）
     * @param descp
     * @throws UnsupportedEncodingException
     */
    @Override
    public String createModel(String key, String name, String module, String descp) throws UnsupportedEncodingException {
        //初始化一个空模型
        Model model = repositoryService.newModel();
        //设置一些默认信息
        String modelName = name;
        String description = descp;
        int revision = 1;
        String modelKey = key;

        ObjectNode modelNode = objectMapper.createObjectNode();
        modelNode.put(ModelDataJsonConstants.MODEL_NAME, modelName);
        modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
        modelNode.put(ModelDataJsonConstants.MODEL_REVISION, revision);
        modelNode.put("module", module);

        model.setName(modelName);
        model.setKey(modelKey);
        model.setMetaInfo(modelNode.toString());

        repositoryService.saveModel(model);
        String id = model.getId();

        //完善ModelEditorSource
        ObjectNode editorNode = objectMapper.createObjectNode();
        editorNode.put("id", "canvas");
        editorNode.put("resourceId", "canvas");
        ObjectNode stencilSetNode = objectMapper.createObjectNode();
        stencilSetNode.put("namespace",
                "http://activiti.org/bpmn");
        editorNode.put("stencilset", stencilSetNode);

        //repositoryService.addModelEditorSource(id, editorNode.toString().getBytes("utf-8"));
        System.out.println("modelId" + id);
        return id;
    }

    /**
     * 查询模型
     *
     * @return
     */
    @Override
    public List<Map<String, Object>> listModel() {
        List<Model> modelList = repositoryService.createModelQuery().list();
        //存放组名的集合
        HashSet<String> modelsSet = new HashSet<>();
        //用于封装给前端的数据
        List<Map<String, Object>> out = new ArrayList<>();
        //分组id
        int groupId = 0;


        //把模块取出来放到set中
        for (Model model : modelList) {
            String metaInfo = model.getMetaInfo();
            Map<String, String> map = JSON.parseObject(metaInfo, Map.class);
            modelsSet.add(map.get("module"));
        }


        //根据模块名对模块进行分组
        for (String modelName : modelsSet) {
            if (StringUtil.isNotEmpty(modelName)) {

                //存放组的map
                Map<String, Object> groupAndModellList = new HashMap<>();
                groupAndModellList.put("id", ++groupId);
                groupAndModellList.put("name", modelName);
                //存放模型的集合
                List<com.nancal.activiti.entity.pojo.Model> models = new ArrayList<>();
                for (Model model : modelList) {
                    String metaInfo = model.getMetaInfo();
                    Map<String, String> metaInfoMap = JSON.parseObject(metaInfo, Map.class);
                    if (StringUtil.isNotEmpty(metaInfoMap.get("module"))) {
                        if (metaInfoMap.get("module").equals(modelName)) {

                            //把原生的model赋值给自定义的model
                            com.nancal.activiti.entity.pojo.Model modelEntity = new ModelEntityImpl();
                            BeanUtils.copyProperties(model, modelEntity);
                            //判断是否已经部署
                            if (StringUtil.isNotEmpty(model.getDeploymentId())) {
                                modelEntity.setDeploymentStatus("已发布");
                            } else {
                                modelEntity.setDeploymentStatus("未发布");
                            }
                            if (modelEntity.hasEditorSource() && modelEntity.hasEditorSourceExtra()) {
                                models.add(modelEntity);
                            }

                        }
                    }
                }
                groupAndModellList.put("children", models);
                out.add(groupAndModellList);
            }

        }
        return out;
    }

    /**
     * 删除部署流程及模板
     * <p>
     * 判断是否部署，
     * 没有部署直接删除模型
     * 已经部署
     * 判断是否有实例(会有不止一个的实例)
     * 有实例，不能删除
     * 没有实例，直接把部署信息与模型删除
     *
     * @param modelId
     */
    @Override
    public String deleteModel(String modelId) {
        Model model = repositoryService.getModel(modelId);
        //判断是否部署
        //没有部署直接删除模型
        if (StringUtil.isEmpty(model.getDeploymentId())) {
            repositoryService.deleteModel(modelId);
        } else {
            //已经部署
            try {
//                ProcessInstance pi = runtimeService.createProcessInstanceQuery().processDefinitionKey(model.getKey()).singleResult();
                //判断是否有实例
                //HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().deploymentId(model.getDeploymentId()).singleResult();
                List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().deploymentId(model.getDeploymentId()).list();

                //有实例，不能删除
                if (list != null && list.size() > 0) {
                    System.out.println("不能删除");
                    return "不能删除";

//                        runtimeService.deleteProcessInstance(pi.getId(), "");
//                        historyService.deleteHistoricProcessInstance(pi.getId());
                }
                //没有实例，直接把部署信息与模型删除
                repositoryService.deleteDeployment(model.getDeploymentId());

            } catch (Exception e) {
                System.out.println("删除流程实例服务异常：");
                e.printStackTrace();
            }
        }
        return "删除成功";
    }

    /**
     * 部署流程
     *
     * @param modelId
     */
    @Override
    public String deployModel(String modelId) throws Exception {
        // 获取模型
        Model modelData = repositoryService.getModel(modelId);
        if (modelData.getDeploymentId()!=null){
            return "该流程已经部署";
        }
        byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());
        if (null == bytes) {
            return "模型数据为空，请先设计流程并成功保存，再进行发布。";
        }
        JsonNode modelNode = objectMapper.readTree(bytes);
        BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
        if (model.getProcesses().size() == 0) {
            return "数据模型不符合要求，请至少设计一条主线程流。";
        }
        byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);


        //发布流程
        String processName = modelData.getName() + ".bpmn20.xml";
        Deployment deployment = repositoryService.createDeployment()
                .name(modelData.getName())
                .addString(processName, new String(bpmnBytes, "UTF-8"))
                .deploy();
        modelData.setDeploymentId(deployment.getId());
        modelData.setKey(modelData.getName() + "_Process_Key");
        repositoryService.saveModel(modelData);
        return "success";
    }


    /**
     * 查询当前用户的任务列表
     *
     * @param processDefinitionId
     * @param taskAssignee
     * @return
     */
    public List<Task> queryTask(String processDefinitionId, String taskAssignee) {

        //3.根据流程定义的key,负责人assignee来实现当前用户的任务列表查询
        List<Task> taskList = taskService.createTaskQuery()
                //.processDefinitionKey(key)
                .processDefinitionId(processDefinitionId)
                .taskAssignee(taskAssignee)
                .list();


        if (taskList.size() > 0) {
            //4.任务列表的展示
//            System.out.println("流程实例ID:" + task.getProcessInstanceId());
//            System.out.println("任务ID:" + task.getId());  //5002
//            System.out.println("任务负责人:" + task.getAssignee());
//            System.out.println("任务名称:" + task.getName());
            return taskList;
        } else {
            System.out.println("没有任务");
            return null;
        }


    }



    /**
     * 查询模板图片
     */
    @Override
    public void queryModelImg(String deploymentId, HttpServletResponse response) {

        InputStream resourceAsStream = null;
        ServletOutputStream outputStream = null;
        try {

            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult();
            resourceAsStream = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());
            // 输出流程图
            outputStream = response.getOutputStream();
            byte[] b = new byte[2048];
            int len;
            while ((len = resourceAsStream.read(b, 0, b.length)) != -1) {
                outputStream.write(b, 0, len);
            }
        } catch (IOException e) {
            throw new RuntimeException("获取流程图出错", e);
        } finally {
            CloseableUtils.close(outputStream, resourceAsStream);
        }
    }


    @Override
    public void queryModelImg2(String deploymentId, HttpServletResponse response) {
        InputStream imageStream = null;
        ServletOutputStream outputStream = null;
        try {

            // 获取model
            Model model = repositoryService.createModelQuery().deploymentId(deploymentId).singleResult();
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(model.getDeploymentId()).singleResult();

            // 获取流程定义信息
            BpmnModel bpmnModel = processRepositoryService.getModel(processDefinition.getId());

            imageStream = processDiagramGenerator.generateDiagram(bpmnModel, "png", "宋体", "宋体", "宋体", null, 1.0);

            // 输出流程图
            outputStream = response.getOutputStream();
            byte[] b = new byte[2048];
            int len;
            while ((len = imageStream.read(b, 0, b.length)) != -1) {
                outputStream.write(b, 0, len);
            }
        } catch (IOException e) {
            throw new RuntimeException("获取流程图出错", e);
        } finally {
            CloseableUtils.close(outputStream, imageStream);
        }
    }


    /**
     * 根据流程实例Id,获取实时流程图片
     *
     * @param processInstanceId
     * @param outputStream
     * @return
     */
    @Autowired
    ProcessEngineConfiguration processEngineConfiguration;

    public void getFlowImgByInstanceId(String processInstanceId, OutputStream outputStream) {
        try {
            if (StringUtils.isEmpty(processInstanceId)) {
                //logger.error("processInstanceId is null");
                return;
            }
            // 获取历史流程实例
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            // 获取流程中已经执行的节点，按照执行先后顺序排序
            List<HistoricActivityInstance> historicActivityInstances = historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId)
                    .orderByHistoricActivityInstanceId().asc().list();
            // 高亮已经执行流程节点ID集合
            List<String> highLightedActivitiIds = new ArrayList<>();
            for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                highLightedActivitiIds.add(historicActivityInstance.getActivityId());
            }

            List<HistoricProcessInstance> historicFinishedProcessInstances = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).finished()
                    .list();
            ProcessDiagramGenerator processDiagramGenerator = null;
            // 如果还没完成，流程图高亮颜色为绿色，如果已经完成为红色
            if (!CollectionUtils.isEmpty(historicFinishedProcessInstances)) {
                // 如果不为空，说明已经完成
                processDiagramGenerator = processEngineConfiguration.getProcessDiagramGenerator();
            } else {
                processDiagramGenerator = new DefaultProcessDiagramGenerator();
            }

            BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
            // 高亮流程已发生流转的线id集合
            List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel, historicActivityInstances);

            // 使用默认配置获得流程图表生成器，并生成追踪图片字符流
            InputStream imageStream = processDiagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivitiIds, highLightedFlowIds, "宋体", "微软雅黑", "黑体", null, 2.0);

            // 输出图片内容
            byte[] b = new byte[1024];
            int len;
            while ((len = imageStream.read(b, 0, 1024)) != -1) {
                outputStream.write(b, 0, len);
            }
        } catch (Exception e) {
            //logger.error("processInstanceId" + processInstanceId + "生成流程图失败，原因：" + e.getMessage(), e);
        }

    }

    @Override
    public String localDeployModel(String processName, String modelName) {
        Deployment deployment = repositoryService.createDeployment()//创建一个部署对象
                .name(processName)
                .addClasspathResource("processes/" + modelName + ".bpmn")
                //.addClasspathResource("processes/"+modelName+".xml")
                .deploy();

        return "success";
    }

    /**
     * 获取已经流转的线
     *
     * @param bpmnModel
     * @param historicActivityInstances
     * @return
     */
    private List<String> getHighLightedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
        // 高亮流程已发生流转的线id集合
        List<String> highLightedFlowIds = new ArrayList<>();
        // 全部活动节点
        List<FlowNode> historicActivityNodes = new ArrayList<>();
        // 已完成的历史活动节点
        List<HistoricActivityInstance> finishedActivityInstances = new ArrayList<>();

        for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
            FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true);
            historicActivityNodes.add(flowNode);
            if (historicActivityInstance.getEndTime() != null) {
                finishedActivityInstances.add(historicActivityInstance);
            }
        }

        FlowNode currentFlowNode = null;
        FlowNode targetFlowNode = null;
        // 遍历已完成的活动实例，从每个实例的outgoingFlows中找到已执行的
        for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) {
            // 获得当前活动对应的节点信息及outgoingFlows信息
            currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
            List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows();

            /**
             * 遍历outgoingFlows并找到已已流转的 满足如下条件认为已已流转： 1.当前节点是并行网关或兼容网关，则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转 2.当前节点是以上两种类型之外的，通过outgoingFlows查找到的时间最早的流转节点视为有效流转
             */
            if ("parallelGateway".equals(currentActivityInstance.getActivityType()) || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) {
                // 遍历历史活动节点，找到匹配流程目标节点的
                for (SequenceFlow sequenceFlow : sequenceFlows) {
                    targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
                    if (historicActivityNodes.contains(targetFlowNode)) {
                        highLightedFlowIds.add(targetFlowNode.getId());
                    }
                }
            } else {
                List<Map<String, Object>> tempMapList = new ArrayList<>();
                for (SequenceFlow sequenceFlow : sequenceFlows) {
                    for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
                        if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
                            Map<String, Object> map = new HashMap<>();
                            map.put("highLightedFlowId", sequenceFlow.getId());
                            map.put("highLightedFlowStartTime", historicActivityInstance.getStartTime().getTime());
                            tempMapList.add(map);
                        }
                    }
                }

                if (!CollectionUtils.isEmpty(tempMapList)) {
                    // 遍历匹配的集合，取得开始时间最早的一个
                    long earliestStamp = 0L;
                    String highLightedFlowId = null;
                    for (Map<String, Object> map : tempMapList) {
                        long highLightedFlowStartTime = Long.valueOf(map.get("highLightedFlowStartTime").toString());
                        if (earliestStamp == 0 || earliestStamp >= highLightedFlowStartTime) {
                            highLightedFlowId = map.get("highLightedFlowId").toString();
                            earliestStamp = highLightedFlowStartTime;
                        }
                    }

                    highLightedFlowIds.add(highLightedFlowId);
                }

            }

        }
        return highLightedFlowIds;
    }

    /**
     * 查询已部署模型
     *
     * @return
     */
    @Override
    public List<Map<String, Object>> listModelDeploy() {
        List<Model> modelList = repositoryService.createModelQuery().list();
        //存放组名的集合
        HashSet<String> modelsSet = new HashSet<>();
        //用于封装给前端的数据
        List<Map<String, Object>> out = new ArrayList<>();
        //分组id
        int groupId = 0;


        //把模块取出来放到set中
        for (Model model : modelList) {
            String metaInfo = model.getMetaInfo();
            Map<String, String> map = JSON.parseObject(metaInfo, Map.class);
            modelsSet.add(map.get("module"));
        }


        //根据模块名对模块进行分组
        for (String modelName : modelsSet) {
            if (StringUtil.isNotEmpty(modelName)) {

                //存放组的map
                Map<String, Object> groupAndModellList = new HashMap<>();

                //存放模型的集合
                List<com.nancal.activiti.entity.pojo.Model> models = new ArrayList<>();
                for (Model model : modelList) {
                    String metaInfo = model.getMetaInfo();
                    Map<String, String> map = JSON.parseObject(metaInfo, Map.class);
                    if (StringUtil.isNotEmpty(map.get("module"))) {
                        if (map.get("module").equals(modelName)) {

                            //把原生的model赋值给自定义的model
                            com.nancal.activiti.entity.pojo.Model modelEntity = new ModelEntityImpl();
                            BeanUtils.copyProperties(model, modelEntity);
                            //判断是否已经部署
                            if (StringUtil.isNotEmpty(model.getDeploymentId())) {
                                modelEntity.setDeploymentStatus("已发布");
                                models.add(modelEntity);
                            } else {
                                modelEntity.setDeploymentStatus("未发布");
                            }


                        }
                    }
                }


                if (models.size() > 0) {
                    groupAndModellList.put("children", models);
                    groupAndModellList.put("id", ++groupId);
                    groupAndModellList.put("name", modelName);
                }
                if (!groupAndModellList.isEmpty()) {
                    out.add(groupAndModellList);
                }


            }

        }
        return out;
    }
}
